package service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.Range;
import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.TextView;

public class TransProcessor {

	private static final Logger LOG = Logger.getLogger(TransProcessor.class
			.getName());

	private static TransProcessor accProcessor = null;
	
	private TransProcessor() {
	};

	/**
	 * Singleton Builder
	 * @return
	 */
	public static TransProcessor getInstance()
	{
		if(accProcessor!=null) return accProcessor;
		
		accProcessor = new TransProcessor();
		return accProcessor;
	}
	
	private static final String WELCOME_TEXT = "Hello, I am a transliteration bot. As and when you are typing I will convert your conversation into Hindi (Right I am supporting only Hindi, but soon will be more generic)";


	/**
	 * 
	 * @param bundle
	 * @param myAddress
	 */
	public void applyProcessorsToChangedBlips(RobotMessageBundle bundle, String myAddress) {
		// Find all affected blips.
		HashMap<Blip, Boolean> changedBlips = new HashMap<Blip, Boolean>();
		for (Event e : bundle.getEvents()) {
			if (!e.getModifiedBy().equals(myAddress)) {
				switch (e.getType()) {

				case WAVELET_SELF_ADDED:
					//this is invoked when I first get added to a conversation
					e.getBlip().createChild().getDocument().append(WELCOME_TEXT);
					break;
				// The blip has been updated. Assume that the user is not yet
				// done editing.
				case BLIP_VERSION_CHANGED:
					changedBlips.put(e.getBlip(), true);
					break;

				// The user has clicked "Done". Assume that the user has
				// finished editing. This is for you Eliza :)
				case BLIP_SUBMITTED: 
					changedBlips.put(e.getBlip(), false);
					break;
				
				default:
					break;
				}
			}
		}

		// Process all affected blips.
		for (Entry<Blip, Boolean> entry : changedBlips.entrySet()) {
				transBlips(entry.getKey(), entry.getValue());
		}
	}

	
	/**
	 * 
	 * @param blip
	 * @param userIsCurrentlyEditing
	 */
	public void transBlips(Blip blip, Boolean userIsCurrentlyEditing) {
		LOG.info("Processing blip " + blip.getBlipId() + " with " + this.getClass().getName());
		// Adapted from Linky bot,
		TextView doc = blip.getDocument();

		Pattern pattern = getCompiledPattern(userIsCurrentlyEditing);
		Matcher matcher = pattern.matcher(doc.getText());
		
		while (matcher.find()) { // if bots adding content, will transliterate entire conversation
			
			String matchedString = matcher.group(1);
			
			LOG.info("Found match to process: " + matchedString);

			int start = matcher.start();
			int end = matcher.end();
			
			processMatch(doc, new Range(start, end), matchedString);
		}
	}

	/**
	 * Take our simple pattern, add some kludges, and compile it.
	 * 
	 * @param userIsCurrentlyEditing
	 *            Is user still editing?
	 */
	private Pattern getCompiledPattern(Boolean userIsCurrentlyEditing) {
		// KLUDGE - If the user is currently editing, try to avoid annotating
		// text while the user's caret is still inside the annotation. For
		// example, imagine that the user types:
		//
		// bug #12|
		//
		// ...where "|" represents the cursor. We could immediately annotate
		// this with a link:
		//
		// [bug #12|]
		//
		// ...but this will tend to make a mess when the user keeps typing:
		//
		// [bug #12 is very annoying|]
		//
		// Instead, we require at least one non-newline character to appear
		// after the match before we try to annotate it. (Note that this hack
		// won't work anywhere but at the end of a paragraph. Users making
		// modifications inside of paragraphs will have to live with minor
		// glitches until the Wave API improves.)
		//
		// To do this, we use a zero-width negative lookahead pattern. But we
		// don't want to use the last character that would normally be matched
		// by getPattern as our negative lookahead, so we use a possessive
		// qualifier to avoid backtracking.
		/*if (userIsCurrentlyEditing)
			return Pattern.compile("(?:" + getPattern() + "){1}+(?!\\r|\\n)");
		else
		*/
		return Pattern.compile(getPattern());
	}

	/**
	 * 
	 * @param doc
	 * @param range
	 * @param name
	 * @param value
	 */
	protected void trans(TextView doc, Range range, String name, String value) {

		try{
			
		LOG.info("Annotating with " + name + "=" + value);
		
		String trans = value;//value.substring(0, value.length() - 2);
		
		LOG.info("Source Trans::" + value);

		String jsonStr = getTransJSON(trans); 
		
		LOG.info("Result from google trans:" + jsonStr);
		
		JSONTokener tok = new JSONTokener(jsonStr);
		JSONObject jsonObj = new JSONObject(tok);
		
		JSONObject obj1 = jsonObj.getJSONObject("txtresults");
		
		JSONArray arry1 = obj1.getJSONArray(value);
		
		/*String rplText = (""+ arry1.get(0)).replaceAll(" ", "");
		rplText = (""+ arry1.get(0)).replaceAll("\n", "");
		rplText = (""+ arry1.get(0)).replaceAll("\r", "");*/
		
		String rplText = ""+arry1.get(0);
		LOG.info("Encoded Code:" + rplText);
		
		LOG.info("Length of the text:" +rplText.length());
		LOG.info("First Range End" +range.getEnd());
		
		//range.setEnd((range.getEnd() > (range.getStart() + rplText.length()))?range.getEnd():range.getStart() + rplText.length());
		
		range.setEnd(range.getEnd() - 2);
		
		LOG.info("The Answer is:" + rplText);
		LOG.info("Range Start" +range.getStart());
		LOG.info("Range End" +range.getEnd());
		LOG.info("NAME:" +name);
		LOG.info("Value:" +rplText);
		
		doc.replace(range, rplText);
		
		}
		catch(IOException ioe)
		{
			LOG.log(Level.SEVERE, ioe.getMessage(), ioe);
			doc.replace(range, value + " {Sorry I am not able to replace at this time.}");
		}
		catch(JSONException jsone)
		{
			LOG.log(Level.SEVERE, jsone.getMessage(), jsone);
			doc.replace(range, value + " {Sorry I am not able to replace at this time.}");
		}
		
		//doc.setAnnotation(range, name, value +ans);
	}	
	

	/**
	 * 
	 * @param source
	 * @return
	 * @throws IOException
	 */
	private String getTransJSON(String source) throws IOException
	{
		// Dont shoot me if it is not legal to do :(
		URL _url = new URL("http://www.google.com/transliterate/indic?tlqt=1&langpair=en|hi&text=" + source +"%2C&tl_app=8&num=5&version=2&jsonp=txtresults");
		URLConnection _con = _url.openConnection();
		BufferedReader buff = new BufferedReader(new InputStreamReader(_con.getInputStream()));
		String results = "";
		String tmpBuff = "";
		while((tmpBuff = buff.readLine())!=null)
		{
			LOG.info("Concatinnating:" + tmpBuff);
			results = results.concat(tmpBuff);
		}
		
		LOG.info("Results Before Convert:" + results);
		
		return convertJSON(results);
	}
	
	/**
	 * <p>Making JSON Lib Happy</p>
	 * @param origJSON
	 * @return
	 */
	private String convertJSON(final String origJSON)
	{
		String orig = new String(origJSON);
		orig = orig.replaceAll("txtresults\\(\\[\\[", "{txtresults:{");
		orig = orig.replaceAll(",\\[", ":[");
		orig = orig.replaceAll("\\]\\],\\]\\)", "]},}}");
		
		return orig;
	}

	protected String getPattern() {
		return "(\\p{Alpha}+)\\s\\S";//"(\\p{Alpha}+)\\s\\S";//"(.+)\\s"; //"(\\d+[*|+|-|/]\\d+=[?])";
	}
	
	protected void processMatch(TextView doc, Range range, String match) {
		trans(doc, range, "calc", match);
	}
}
